////let 变量名：类型=初始值;
//变量名 可以包含 字母数字_$
//数字不能开头


let has: boolean = true;  //let 变量名：类型=初始值

// let 2has:boolean=true;   //报错  数字不能开头

// let has$:boolean=false;

// let _has:boolean=true;

// let $has:boolean=true;

console.log(has);

has = false;

console.log(has);


// has="你好"; //报错 因为has是boolean类型 不能赋值字符串


//has=123; // 报错

has = !!"你好";

console.log(has);





//number数字类型

let m: number = 200;

//m="hello"; 报错

m = -200.2;

console.log(m);


//string类型

let str: string = "hello world";

console.log(str);

//str=10000; 报错

// str=false; 报错
// ""  ''  ``里面不管放任何东西都是字符串
str = `
 <div></div>
 var m=1000;
`

//定义数组类型

//  变量:类型[]=初始值；

//  变量：Array<类型>=初始值；

let arr: number[] = [1, 2, 3, 4, 5];

// arr=["a","b"] //报错

let arr2: Array<string>;

// arr2=[1,2,3] 报错

arr2 = ["1", "2", "3"];

console.log(arr2);

//元组数据类型

let arr3: [number, number, string, boolean] = [1, 2, "a", true];


//枚举  enum
console.log("--------枚举------------");

enum ColorEnum {
    Red, //标识符
    Yellow=100, //标识符
    Blue, //标识符
    Black="黑色", //标识符
    White="白色"
}

let color: ColorEnum = ColorEnum.Red;

console.log(color); //输出1 如果标识符没有赋值，这个标识符的值是下标


color=ColorEnum.Black;

console.log(color);  //输出 黑色


color=ColorEnum.Yellow;

console.log(color); //输出100

color=ColorEnum.Blue;

console.log(color); //输出101

color=ColorEnum.White;

console.log(color);


function getColorValue(val: ColorEnum) {

    switch (val) {
        case ColorEnum.Blue:
            console.log("蓝色");
            break;
        case ColorEnum.Black:
            console.log("黑色");
            break;
        case ColorEnum.Yellow:
            console.log("黄色");
            break;
        case ColorEnum.Red:
            console.log("红色22");
            break;
        case ColorEnum.White:
                console.log("白色2");
                break;
        default:
            break;
    }

}

color=ColorEnum.Red;
  getColorValue(color);


  //any 类型

  console.log("-------any类型--------");

  let obj:any;

  obj=10000;
  console.log(obj);

  obj="你好";

  console.log(obj);

//unknown 类型
console.log("-------unknown类型--------");

 let obj2:unknown;

 obj2=20000;
 console.log(obj2);

 obj2="你好世界";
 console.log(obj2);


 //any  unknown的区别

 obj="hello world";

 console.log(obj);

 let hello:string;


 hello=obj;
//  hello=obj2;// 报错 unknown类型不能给 别变量赋值

 console.log("hello:",hello);
 
 

 
//null \undefined类型
console.log("--------null  undefined类型---------");

let info:undefined;

info=undefined;

// info="zs"; 报错

// info=100; 报错


let info2:null;

info2=null;

//联合类型  类型1 |（管道符）  类型2 |....

console.log("--------联合类型---------");


let val:string | number | undefined | null;

val=100;
console.log(val);

val="你好";
console.log(val);

 val=undefined;
console.log(val);

val=null;
console.log(val);


//void 
console.log("----------void类型-  函数没有返回值---------");

function submit():void{

    console.log("这是一个没有返回值的函数");
    
    // return 10000; 报错

    // return "ok"; 报错

}

submit();

  
  
//never类型
console.log("---------never类型-----------");

function error():never{


    throw new Error("错误");

    console.log("执行后面代码");
    
    

}

//  error();

//无限循环  死循环

function loop(): never {
    while (true) {

        console.log("执行");
        
    }

    console.log("执行结束");
    
}

// loop();

//类型推断  声明变量的时候 不给它指定类型 

console.log("-------类型推断------");

//1、基本类型推断
 
let x=100;  //推断x为 number类型；

console.log(x);

// x="你好" //报错

let  userName="张三"; //推断为 string类型

// userName=100; 报错


//数组类型推断

let arr5=[1,2,3]; //推断arr5类型为 number[]

// arr5=["a","b"] 报错

//特殊


let arr6=[1,2,3,null];

arr6=[null,2,3];

 


// ts函数定义

console.log("--------函数定义---------");

 

// function 函数名(参数1:类型,参数2:类型):返回类型{

//     return 返回值
// }
//声明方式 定义函数
function getUserInfo(name:string):string{

     return "姓名"+name;

    // return 100; //报错 函数返回值是string

}
//表达式 定义函数
let getInfo=function(name:string):string{
    return "姓名"+name;
}

//箭头函数

let getUser=(name:string):string=>{
    return "姓名"+name;
}

// let zs= getUserInfo(100);  报错
let zs= getUserInfo("张三");

console.log(zs);


function add():void{

 
}

//ts 函数的可选参数  age为可选参数

function update(name:string,age?:number,from?:string):void{

    console.log(name);
    console.log(age);
    console.log(from);

}


// update("李四",18,"北京"); 报错

update("李四",20,"北京");


function add2(age:number,name?:string):void{

    console.log(age);
    console.log(name);

}

add2(20,"王五");

//函数的默认参数

function getStudent(name:string,from:string="中国",id:number=0){
    console.log("name",name);
    console.log("id",id);
    console.log("来自",from);

   
    return id;
    

}

let id:number=getStudent("赵六");

//函数剩余参数 剩余参数必须在参数最后

function sum(a:number,...c:number[]):number{

    console.log("c",c);
    
    return a+c.reduce((pre,cur)=>pre+cur); //reduce求和函数

 

}

let s=sum(1,2,3,4,5,6,7,8,9);
console.log(s);


//函数重载  函数名一样 参数不一样

//js（es5） 函数名一样  后面的函数会覆盖掉前面的函数

//ts重载 函数名一样 函数参数不一样

console.log("----------函数重载---------");

function getStudentInfo(name:string):string;
function getStudentInfo(age:number):string;

function getStudentInfo(nameOrAge:string|number):string{

    if(typeof nameOrAge==="string"){

        return "姓名"+nameOrAge;
    }
    else{
        return "年龄"+nameOrAge;
    }

}

let zs2= getStudentInfo("张三");
console.log(zs2);

let age=getStudentInfo(18);

console.log(age);

// getStudentInfo(true); 报错

//函数的类型推断

console.log("----------函数的类型推断---------");

function getStudentInfo2(from="中国"){

    console.log("来自",from);
    
   
}

// getStudentInfo2(100); 报错

getStudentInfo2();
getStudentInfo2("小日子");

//函数返回值 类型推断

function getStudentInfo3(name:string){

    return "姓名"+name;
}

var zs3:string;

zs3=getStudentInfo3("张三");

console.log(zs3);

// let val2:number=getStudentInfo3("张三");  报错 
 

console.log("----------类 class---------");
 
//定义类  class

//constructor作用：初始化属性

class Student{
  
     //属性
     name:string;
     age:number;
     constructor(name:string,age:number){
         this.name=name;
         this.age=age;
     }
     //方法
     getName():string{
         console.log("我叫",this.name);
         return this.name;
     }
     getAge():number{
         return this.age;
     }
     
}

let zhangsan= new Student("张三",18);

console.log(zhangsan.name);

console.log(zhangsan.age);

let zsName= zhangsan.getName();
console.log(zsName);

//类的继承  子类继承父类  子类拥有父类的属性和方法（除了私有属性） 继承关键字 extents

console.log("----------类继承---------");


//定义一个动物类 父类
class Animal{
    name:string;
    color:string;
    constructor(name:string,color:string){
        this.name=name;
        this.color=color;
    }
   cry():void{
        console.log(this.name+ "汪汪汪");
    }

}
//子类Dog 继承了父类
class Dog extends Animal{
    age:number;
    constructor(name:string,color:string,age:number){
        super(name,color);  //调用父类的构造函数
        this.age=age;
    }
    dogCry():void{

        //子类调用父类的方法
        super.cry();
        
    }
}

let wangcai=new Dog("旺财","白色",2);

console.log(wangcai.name);

// wangcai.cry();

wangcai.dogCry();


//类的修饰符  public
console.log("----------类修饰符---------");
class Person{
    public name:string;  //public 公共属性
    protected sex:string; //protected 受保护的属性
    private age:number;
    constructor(name:string,age:number,sex:string){
        this.name=name;
        this.sex=sex;
        this.age=age;
    }
   static  print(){
        console.log("父类的静态方法");
    }
    getAge():number{
        console.log("年龄",this.age);

        return this.age;    
    }
    getName():string{
        return this.name;
    }
}

class Child extends Person{
    constructor(name:string,age:number,sex:string){
        super(name,age,sex);
    }
    getSubAge() {
        
        // console.log(this.age); 报错 子类不能访问父类的私有属性
        
        
    }
    getSex():string{
        //子类访问父类的属性
        return this.sex;
    }
}

//public   公有的属性   外面可以使用  子类也可以使用 本类(类的内部)也可以使用

//protected   受保护的属性 外面不可以使用  子类可以使用  本类(类的内部)也可以使用

// private   私有属性   外面不可以使用  子类不可以使用  本类(类的内部)可以使用

let zhaoliu=new Person("赵六",18,"男");

console.log(zhaoliu.name); //可以访问

// console.log(zhaoliu.age); //报错 age是父类的私有属性，外界无法访问


// console.log(zhaoliu.sex); //报错 外面不能访问

zhaoliu.getAge();

let  wangwu=new Child("王五",18,"男");

// console.log(wangwu.sex); 报错 sex是父类的受保护的属性，外界无法访问

let sex= wangwu.getSex();
console.log(sex);

Child.print(); //子类继承父类的静态成员


//静态属性
console.log("----------静态属性---------");

class Person2{
   static from:string="来自中国";
   name:string;
    constructor(name:string){
        this.name=name;
    }
    static print(){

        console.log("这是一个静态方法");
        
      
        
    }
}

//访问静态成员（属性和方法）  类名.成员


console.log(Person2.from);
Person2.from="来自美国";
console.log(Person2.from);
Person2.print();



//抽象类 使用abstract定义抽象类
console.log("----------抽象类---------");
abstract class AnimalManager{
    name:string;
    constructor(name:string){
        this.name=name;
    }
    abstract cry():void; //抽象方法 不包含具体实现

   
}

class Cat extends AnimalManager{
    constructor(name:string){
        super(name);
    }
    cry(): void {
        console.log("喵喵喵");
        
    }
    
}

let xiaohei= new Cat("小猫");

xiaohei.cry();

class Dog2 extends AnimalManager{
    constructor(name:string){
        super(name);
    }
    cry(): void {
        console.log("汪汪汪");
        
    }
    
}

let wc=new Dog2("旺财");
wc.cry();

//类可以实现多继承

//接口  定义 使用 interface
console.log("----------接口---------");

//1、属性接口    传入对象 约束

interface IStudent{
    name:string,
    age?:number
}

const getStuInfo=(student:IStudent)=>{
  
    console.log(student.name,student.age);
    
}
// getStuInfo({id:100}); 报错

getStuInfo({name:"张三",age:18})


const zl:IStudent={age:18,name:"张三"}

console.log(zl);

//2、函数类型接口
interface IAdd{
    (num:number,num2:number):number
}
 

// const addFn:IAdd=(num:number,num2:string)=>{
//     return num+num2;
// }
// //报错


const addFn:IAdd=(num:number,num2:number)=>{
    return num+num2;
}

console.log(addFn(1,2));



//3、可索引接口    约束数组对象

interface IArr{
    [index:number]:string
}

// let arr8:IArr=[1,2,3,4] 报错 数组项是string 类型

let arr9:IArr=["1","2","3"];
console.log(arr9);

interface IObject{
    [index:string]:string
}

// var userOject:IObject={
//     name:"张三",
//     age:18
// }  
// 报错 因为对象的值是string类型

var userOject:IObject={
    name:"张三",
    age:"18"
}  

//类 类型接口

interface IClass{
    name:string;
    age:number;
    cry():void;
    getName?():string;
}

interface IClass2{
    from:string;
  
}
//一个类可以实现多个接口
class Person3 implements IClass,IClass2{ 
    name:string;
    age:number;
    from:string;
    constructor(name:string,age:number){
        this.name=name;
        this.age=age;
        this.from="中国";
    
    }
    cry(): void {
        console.log("cry");
        
    } 

}

//接口继承
interface IAnimal{
    name:string;
    age:number;
    cry():void;
}
interface IAnimal2{
    color:string;
}

interface ICat extends IAnimal,IAnimal2{
    hobby:string;
}

let xiaobai:ICat={
    name:"小白",
    age:18,
    color:"白色",
    hobby:"吃东西",
    cry(): void {
        console.log("喵喵喵");
    }

}


class  Cat2 implements ICat{
    hobby: string;
    name: string;
    age: number;
    color: string;

    constructor(hobby:string){ 
        this.hobby=hobby;
        this.name="小猫";
        this.age=18;
        this.color="白色";
    }
    cry(): void {
        console.log("喵喵喵");
        
    }

}


//

interface IData{
    code:number,
    msg:string,
    data:IDataItem[]
}

interface IDataItem{
    id:number,
    name:string,
    children?:IDataItem[]
}

const responseData:IData= {
    code: 200,
    msg: "请求成功",
    data: [
              {
                  id: 1,
                  name: "流星花园",
                  children: [
                      {
                          id: 3,
                          name: "1栋",
                          children:[
                             {
                                  id: 8,
                                   name: "1单元",
                             }
                          ]
                      },
                      {
                          id: 4,
                          name: "2栋"
                      }
                  ]
              },
              {
                  id: 2,
                  name: "清水湾",
              }
          ]
  };
  
  interface INews{
      id:number,
      title:string,
      content?:string
  }

  let arr10:INews[]=[{id:1,title:"北京"},{id:2,title:"上海"}]

  console.log("----------泛型-------------");  
//泛型  定义  类 接口  函数的时候 不指定类型，传入再指定类型   <>传入 

//any和泛型的区别  any没有类型校验 泛型有类型校验
//1、泛型函数

function  identity<T>(arg:T):T{

   return arg;

}
 
let output = identity(10000)
console.log(output);

let output2 = identity<string>("10000");


//封装一个泛型函数   返回数组的长度

function  getArrayLength<T>(arg:T[]):number{

   return arg.length;

}
console.log(getArrayLength([1,2,3,4,5]));
console.log(getArrayLength(["a","b","c"]));

//泛型函数
function Md5<T,V>(key:T,value:V):T{

     return key

}

let output3 = Md5<string,number>("key",10000);

console.log(output3);

//使用泛型封装冒泡排序函数

let arr11=[1,3,5,8,2,9];

let arr12=["b","c","a","d"];

function bubbleSort<T>(ary:T[]):T[]{

    for(var i=0; i<ary.length; i++){//正在进行第几次循环
        for(var j=0; j<ary.length-1-i; j++){//本次循环需要执行几次
            if(ary[j]>ary[j+1]){
                var tmp=ary[j];
                ary[j]=ary[j+1];
                ary[j+1]=tmp;
            }
        }
    }
    return ary;


}
console.log(arr11);
console.log(bubbleSort(arr11));

console.log(arr12);
console.log(bubbleSort(arr12));

//泛型 类

console.log("----------泛型类----------");


class StudentData<T>{
   list:T[];
   constructor(){
       this.list=[];
   }
   add(data:T){

    this.list.push(data);

   }
   remove(index:number):T[]{

     this.list.splice(index,1);

     return this.list;
      
   }
   getList():T[]{
       return this.list;
   }

}


// let arr13=[1,2,3,4,5];

 
  const st= new StudentData<IStudent>();

  st.add({name:"张三",age:18});
  st.add({name:"李四",age:20});

  console.log(st.list);

  st.remove(0);

  console.log(st.list);

  //泛型接口

  interface IData2<T,V>{
      key:T,
      value:V
  }


 var obj3:IData2<string,number>={key:"name",value:1000}


 //函数类型接口 泛型

 interface encrypt {
    <T,V>(key: T, value: V): T;
  }
  var md5: encrypt = function<T,V> (key: T, value: V): T {
  
    return key;
  }


//泛型约束  参数类型 满足某些条件    关键字  extents

console.log("----------泛型约束----------");


interface ILength {
    length:number;
}

function getDataLength<T extends ILength>(data:T):number{

    return data.length;

}

console.log(getDataLength("hello"));

// console.log(getDataLength(10)); 报错 10没有 length属性


console.log(getDataLength(["a","b"]));

//实现传入数组必须有id，name 属性   
interface IUserModel{
    id:number;
    name:string;
}
 function getUserList<T extends IUserModel>(arr:T[]):T[]{

    return arr;

 }

console.log(getUserList([{id:1,name:"张三"},{id:2,name:"李四"}]));

// console.log(getUserList([{id:1,label:"张三"},{id:2,label:"李四"}]));  报错 没有 name属性

// console.log(getUserList(["a","b"])); 报错 没有id name属性


//有一个对象  根据 键返回 它值

 function getDataValue<T,K extends keyof T>(obj:T,key:K){

   return obj[key];

 }
//keyof T T所有的属性
//V extends keyof T  ：V继承 T的属性

let outputKey=getDataValue({id:1,name:"张三111"},"name");

// let outputKey=getDataValue({id:1,name:"张三111"},"age"); 报错  没有 age属性

console.log(outputKey);

 
 //泛型 默认类型

 interface IData3<T,V=string>{
    value:T;
    label:V;
 }

var obj4:IData3<string,number>={value:"1000",label:20000};

console.log(obj4);



// var obj5:IData3<string>={value:"1000",label:20000}; 报错 label没指定类型 默认为 string
var obj5:IData3<string>={value:"1000",label:"200000"};
console.log(obj5);


//类型别名 type
console.log("----------类型别名----------");

//基本数据类型
type count=number;

let n:count=100;

console.log(n);

// n="10000";  报错

type strType=string;

let str18:strType="hello";

console.log(str18);

//对象类型
type  UserObject={
    id:string,
    name:string
}

let user:UserObject={
    id:"1000",
    name:"张三"
}

//type 、interface的区别

//1、type可以定义基本数据类型| 对象类型、interface定义对象

//函数类型接口
// interface GetUser{
//     (val:string):string
// }


//type 函数
type GetUser=(val:string)=>string;

const getUser2:GetUser=(val:string)=>{

    return val;

    
}

//type 元组

type Tuple=[string,number];

let tuples:Tuple=["a",10000];


//联合类型

type UnionType=string|number;

let  parmas:UnionType="hello";

parmas=10000;

type  UnionType2={id:number} | {name:string};

let  parmas2:UnionType2={id:1000};

parmas2={name:"张三"};

parmas2={id:100,name:"张三"};

console.log(parmas2);

//type扩展
type  UserType={
    id:number,
    name?:string
}

type  UserType3={
    hobby:string[]
}

interface  IUserType4{
    sex:string
}

type  UserType2=UserType & UserType3 & IUserType4 & {age:number}

let user2:UserType2={
    id:100,
    name:"张三",
    age:18,
    hobby:["a","b"],
    sex:"男"
}

//interface扩展 type
interface IUserType6 extends UserType2{

    from:string
}

let user3:IUserType6={
    id:100,
    name:"张三",
    age:18,
    hobby:["a","b"],
    sex:"男",
    from:"中国"
    
   
}

//interface和type 区别

 //1、interface支持声明合并  tyep不支持

 interface IStore{
    id:number,
    name:string
}

interface IStore{
    name:string,
    address:string
}
 

let store:IStore={
    id:100,
    name:"FCK",
    address:"北京"
    
}


// type StoreType={
//     id:number,
//     name:string
// }
 
// type StoreType={
//     address:string
// }
//  报错 StoreType声明了两次

//

//class可以实现interface,type

// type AnimalType={
//     name:string,
//     age:number,
//     color:string
// }
interface AnimalType{
    name:string,
    age:number,
    color:string
}

class AnimalModel implements AnimalType{
    name:string;
    age:number;
    color:string;
    constructor(name:string,age:number,color:string){
            this.name=name;
            this.age=age;
            this.color=color;
   }
    
}

//类型断言  类型转换 <>    as

console.log("----------类型断言----------");

let  str19:unknown="hello";

let len:number=(<string>str19).length;

let len2:number=(str19 as string).length;

// let len3:number=str19.length; 报错 unknown类型没有length属性


interface IHouse{
    id:number,
    name:string
}
 
const house={};

// house.name="北京"; 报错 house没有name属性

(house as IHouse).name="北京";

  
console.log((house as IHouse).id);

console.log( (<IHouse>house).name);

//readonly 只读属性 

interface ICompany{
    readonly  id:number,
    name:string
}

let company:ICompany={
     id:100,
    name:"百度"
}

// company.id=200; 报错 id是只读属性
company.name="阿里";

console.log(company);


//const断言

let point=[1,2] as const;
// point[0]=100; 报错  point只读数组

console.log(point);

//非空断言运算符（!）


let token:string;

sessionStorage.setItem("token","123456");

token=sessionStorage.getItem("token")!;

console.log(token);


let username:string|undefined;


function setValue(){ 

    username="zs"

}
setValue();
// let nameLen:number=username.length; 报错 username可能为undefined
let nameLen:number=username!.length;

 
//双重断言   两次转换

let age8:number=18;

let ageValuae:string=age8 as any as string;

console.log(ageValuae);

 


